using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using RacePlatformer.Manager;
using FarseerPhysics.Dynamics;
using FarseerPhysics;
using RacePlatformer.Support;
using RacePlatformer.Object;
using RacePlatformer.Object.Player;
using FarseerPhysics.DebugViews;
using Dwarfs.Manager;
using System.Threading;
using Dwarfs.Object.Boss;

namespace RacePlatformer
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class MainGame : Microsoft.Xna.Framework.Game
    {
        public enum GameState { INTRO, CHARACTER_SELECT, INGAME, LEVEL_PICK, END, PAUSE, MENU }
        public GameState currentGameState;
        public int currentLevel = 1;
        public int amountofLevels = 2;
        private Boss boss;
        //graphics
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        //XNA PHYSICS DEBUG
        DebugViewXNA DebugView;
        private bool debugViewEnabled = true;
        private bool Debugging = true;
        KeyboardState previousKeyboardState;
        //Game objects
        public GameObjectManager goManager;
        public BackgroundManager bgManager;
        public PlayerManager pManager;
        public ScreenManager screenManager;
        public SoundManager soundmanager;
        World world;
        //PLAYER
        public BasePlayer leader;
        //CAMERA
        public Camera2D camera;
        public const int SCREEN_WIDTH = 1280;
        public const int SCREEN_HEIGHT = 720;
        public Vector2 topLeftOfScreen;
        public HUD hud;
        public MainGame()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            setResolution(SCREEN_WIDTH, SCREEN_HEIGHT);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            soundmanager = new SoundManager(this);
            if (Debugging)
            {
                inializeGame();
            }
            else
            {
                startGame();
            }
            base.Initialize();
        }
        protected void startGame()
        {
            currentGameState = GameState.INTRO;
            screenManager = new ScreenManager(this);
            soundmanager.loadContent(currentLevel);
            soundmanager.loadScreenSounds();
        }
        public void inializeGame()
        {
            currentGameState = GameState.INGAME;
            screenManager = new ScreenManager(this);
            soundmanager.loadScreenSounds();
            soundmanager.loadContent(currentLevel);
            world = new World(Vector2.Zero);
            world.Gravity = new Vector2(0f, 100f);
            // TODO: Add your initialization logic here
            bgManager = new BackgroundManager(this);
            goManager = new GameObjectManager(this, world, currentLevel);
            bgManager.initialize(currentLevel);
            hud = new HUD(this);
            setDebugStuff();
            initLevel(1);
        }
        public void initLevel(int newLevel)
        {
            destoryPlayers(currentLevel);
            goManager.destoryAllObjects(currentLevel);
            currentLevel = newLevel;
            soundmanager.loadContent(currentLevel);
            bgManager.initialize(newLevel);
            goManager = new GameObjectManager(this, world, newLevel);
            createCamera();
            createBoss(newLevel);
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        public void switchLevel(int newLevel)
        {
            hud.hudEnabled = false;
            initLevel(newLevel);

        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState currentKeyboardState = Keyboard.GetState();
            switch (currentGameState)
            {
                //INTRO STATE
                case GameState.INTRO:
                    screenManager.introScreen.UpdateMenu();
                    break;
                case GameState.MENU:
                    screenManager.menuScreen.UpdateMenu();
                    break;
                //LEVEL PICK STATE
                case GameState.LEVEL_PICK: break;
                //CHARACTER SELECT STATE
                case GameState.CHARACTER_SELECT: break;
                //INGAME STATE
                case GameState.INGAME:
                    soundmanager.playBackgroundSound();
                    goManager.Update(gameTime);
                    //THIS IS REQUIRED TO TURN ON GRAVITY
                    world.Step(0.0167f);
                    //UPDATE CAMERA
                    camera.Update(gameTime);
                    hud.Update(gameTime);
                    if (currentKeyboardState.IsKeyDown(Keys.M))
                    {
                        camera.Zoom *= 0.9f;
                    }
                    if (currentKeyboardState.IsKeyDown(Keys.N))
                    {
                        camera.Zoom *= 1.1f;
                    }
                    if (currentKeyboardState.IsKeyUp(Keys.F4) && previousKeyboardState.IsKeyDown(Keys.F4))
                    {
                        debugViewEnabled = !debugViewEnabled;
                    }
                    if (currentKeyboardState.IsKeyUp(Keys.F9) && previousKeyboardState.IsKeyDown(Keys.F9))
                    {
                        SaveStateManager.loadLastState();
                    }
                    if (currentKeyboardState.IsKeyUp(Keys.F7) && previousKeyboardState.IsKeyDown(Keys.F7))
                    {
                        if (currentLevel < amountofLevels)
                            switchLevel(++currentLevel);
                        else
                            switchLevel(1);
                    }
                    if (currentKeyboardState.IsKeyUp(Keys.Escape) && previousKeyboardState.IsKeyDown(Keys.Escape))
                    {
                        SoundManager.stopBackgroundSound();
                        currentGameState = GameState.PAUSE;
                    }
                    //update player action contorller
                    foreach(BasePlayer player in PlayerManager.players)
                    {
                        player.Update(gameTime);
                    }
                    if (pManager != null)
                    {
                        pManager.update(gameTime);
                    }
                    break;
                //PAUSE STATE
                case GameState.PAUSE:
                    screenManager.pauseScreen.UpdateMenu();
                    break;
                //END STATE?
                case GameState.END: break;
            }
            previousKeyboardState = currentKeyboardState;
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            switch (currentGameState)
            {
                //INTRO STATE
                case GameState.INTRO:
                    spriteBatch.Begin();
                    screenManager.introScreen.DrawScreen(gameTime, spriteBatch);
                    spriteBatch.End();
                    break;
                //MENU STATE
                case GameState.MENU:
                    spriteBatch.Begin();
                    screenManager.menuScreen.DrawScreen(gameTime, spriteBatch);
                    spriteBatch.End();
                    break;
                case GameState.PAUSE:
                    spriteBatch.Begin();
                    screenManager.pauseScreen.DrawScreen(gameTime, spriteBatch);
                    spriteBatch.End();
                    break;
                //LEVEL PICK STATE
                case GameState.LEVEL_PICK:
                    spriteBatch.Begin();
                    spriteBatch.End();
                    break;
                //CHARACTER SELECT STATE
                case GameState.CHARACTER_SELECT:
                    spriteBatch.Begin();
                    spriteBatch.End();
                    break;
                //INGAME STATE
                case GameState.INGAME:
                    spriteBatch.Begin(SpriteSortMode.BackToFront, null, null, null, null, null, camera.View);
                    // TODO: Add your drawing code here
                    goManager.Draw(gameTime, spriteBatch);
                    if (debugViewEnabled)
                    {
                        Matrix projection = camera.SimProjection;
                        Matrix view = camera.SimView;
                        DebugView.RenderDebugData(ref projection, ref view);
                    }
                    //draw players
                    foreach (BasePlayer player in PlayerManager.players)
                    {
                        player.Draw(gameTime, spriteBatch);
                    }
                    hud.draw(gameTime, spriteBatch);
                    bgManager.drawBackgrounds(gameTime, spriteBatch);
                    spriteBatch.End();
                    break;
            }
            base.Draw(gameTime);
        }
        private void setResolution(float width, float height)
        {
            graphics.PreferredBackBufferWidth = (int)width;
            graphics.PreferredBackBufferHeight = (int)height;
            graphics.ApplyChanges();
        }
        private void createCamera()
        {
            camera = new Camera2D(this.GraphicsDevice, this);
            camera.MinRotation = -0.05f;
            camera.MaxRotation = 0.05f;
        }
        private void setDebugStuff()
        {
            DebugView = new DebugViewXNA(world);
            DebugView.AppendFlags(DebugViewFlags.Shape);
            DebugView.AppendFlags(DebugViewFlags.PolygonPoints);
            DebugView.DefaultShapeColor = Color.White;
            DebugView.SleepingShapeColor = Color.LightGray;
            DebugView.LoadContent(this.GraphicsDevice, this.Content);
        }
        public void createBoss(int level)
        {
            boss = new Boss(this, world, new Vector2(700, 0), 99, level);
            goManager.addObject(boss);
            // boss.launchBoss(new Vector2(-5000, 0));
            boss.moveBoss(new Vector2(-6, boss.body.LinearVelocity.Y));
            camera.EnableRotationTracking = false;
            camera.TrackingBody = boss.body;
            camera.EnableTracking = true;
        }
        public void createPlayers(int level, int amount, Vector2 position)
        {
            pManager = new PlayerManager(this);
            pManager.hasControl = true;
            for (int i = 0; i < amount; i++)
            {
                BasePlayer tempPlayer = null;
                switch (i)
                {
                    case 0:
                        tempPlayer = new Daan(this, world, new Vector2(position.X, position.Y), 3, level, Keys.F);
                        PlayerManager.playerGroupA.Add(tempPlayer);
                        break;
                    case 1:
                        tempPlayer = new Cassandra(this, world, new Vector2(position.X, position.Y), 3, level, Keys.D);
                        PlayerManager.playerGroupB.Add(tempPlayer);
                        break;
                    case 2:
                        tempPlayer = new Daan(this, world, new Vector2(position.X, position.Y), 3, level, Keys.S);
                        PlayerManager.playerGroupA.Add(tempPlayer);
                        break;
                    case 3:
                        tempPlayer = new Cassandra(this, world, new Vector2(position.X, position.Y), 3, level, Keys.A);
                        PlayerManager.playerGroupB.Add(tempPlayer);
                        break;
                }
                PlayerManager.players.Add(tempPlayer);
                tempPlayer.body.ApplyLinearImpulse(new Vector2(2, -2));
            }
            camera.TrackingBody = PlayerManager.players[PlayerManager.players.Count - 1].body;
            hud.hudEnabled = true;
            topLeftOfScreen = new Vector2(PlayerManager.players[PlayerManager.players.Count - 1].body.Position.X - (SCREEN_WIDTH / 2),
               PlayerManager.players[PlayerManager.players.Count - 1].body.Position.Y - (SCREEN_HEIGHT / 2));
        }
        public void destoryPlayers(int level)
        {
            for (int i = 0; i < PlayerManager.players.Count; i++)
            {
                PlayerManager.players[i].body.Dispose();
                PlayerManager.players[i].feetBody.Dispose();
                PlayerManager.players[i] = null;
            }
            PlayerManager.initialize();
        }
    }
}
